package controller;

import model.ModelFunctions;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import commons.Entities.Comment;
import commons.Entities.LegenCharacter;
import commons.Entities.LegenDate;
import commons.Entities.Review;
import commons.Entities.User;
import commons.Exceptions.characterWithNoNameException;
import commons.Exceptions.dateAlreadyTakenException;
import commons.Exceptions.userAlreadyExistsException;
import commons.Exceptions.userDoesntExistException;
import commons.Exceptions.userWithNoNameException;
import commons.Exceptions.userWithNoPasswordException;
import commons.Exceptions.wrongPasswordException;
import commons.Exceptions.wrongRePasswordException;

public class ControllerFunctions {

	private static final Log logger = LogFactory.getLog(ControllerFunctions.class);

	private final static ControllerFunctions INSTANCE = new ControllerFunctions();

	public static ControllerFunctions get() {
		return INSTANCE;
	}

	public ControllerFunctions() {
	}

	/**
	 * Searches the user's name in DB and Compares DB password with given one
	 * 
	 * @param userName
	 * @param password
	 * @return user's details
	 * @throws userDoesntExistException
	 * @throws wrongPasswordException
	 */
	public User login(String userName, String password) throws userDoesntExistException, wrongPasswordException {

		User user = ModelFunctions.get().getUserFromDB(userName);

		if (user == null) {
			logger.info("Username was not found");
			throw (new userDoesntExistException(userName));
		}

		if (!user.getPassword().equals(password)) {
			logger.info("Worng password accepted");
			throw (new wrongPasswordException());
		}

		logger.info("Logged in");
		return user;
	}

	/**
	 * Searches the user's name in DB
	 * 
	 * @param userName
	 * @return user's details
	 */
	public User getUserDetails(String userName) {
		return ModelFunctions.get().getUserFromDB(userName);
	}

	/**
	 * Checks if the user exists Inserts or updates user details to DB
	 * 
	 * @param user
	 * @param rePassword
	 * @throws wrongRePasswordException
	 * @throws userWithNoPasswordException
	 * @throws userWithNoNameException
	 * @throws userAllreadyExistsException
	 */
	public void saveOrUpdateUser(User user, String rePassword, boolean newUser) throws wrongRePasswordException, userAlreadyExistsException, userWithNoPasswordException, userWithNoNameException {

		if (newUser) {
			if (ModelFunctions.get().getUserFromDB(user.getUserName()) == null) {
				if (user.getUserName().equals("")) {
					logger.info("User must have a name");
					throw (new userWithNoNameException());
				}
				if (user.getPassword().equals("")) {
					logger.info("User must have a password");
					throw (new userWithNoPasswordException());
				}
				if (!rePassword.equals(user.getPassword())) {
					logger.info("Worng re-password accepted");
					throw (new wrongRePasswordException());
				}
				ModelFunctions.get().addNewUserToDB(user);
			} else {
				logger.info("Used name for user");
				throw (new userAlreadyExistsException(user.getUserName()));
			}
		} else {
			if (!rePassword.equals(user.getPassword())) {
				logger.info("Worng re-password accepted");
				throw (new wrongRePasswordException());
			}
			ModelFunctions.get().updateUserInDB(user);
			logger.info("User details were updated to DB");
		}
	}

	/**
	 * get the user's favorites list from DB
	 * 
	 * @param userID
	 * @return list of favorite characters
	 */
	public void getUserFavorites(Integer userID) {
		ModelFunctions.get().getUserFavoritesFromDB(userID);
	}

	/**
	 * add a favorite
	 * 
	 * @param userID
	 * @param characterID
	 */
	public void addFavoriteToDB(Integer userID, Integer characterID) {
		ModelFunctions.get().addFavoriteToDB(userID, characterID);
	}

	/**
	 * remove a favorite
	 * 
	 * @param userID
	 * @param characterID
	 */
	public void removeFavoriteFromDB(Integer userID, Integer characterID) {
		ModelFunctions.get().removeFavoriteFromDB(userID, characterID);
	}

	/**
	 * get the user's date list
	 * 
	 * @param userID
	 * @return list of user's dates
	 */
	public void getUserLegenDates(Integer userID) {
		ModelFunctions.get().importUserLegenDates(userID);
	}

	/**
	 * Updates dating table with the new date
	 * 
	 * @param legenDate
	 * @throws dateAlreadyTakenException
	 */
	public void setLegenDate(LegenDate legenDate) throws dateAlreadyTakenException {
		if (ModelFunctions.get().checkIfDateIsTaken(legenDate)) {
			throw (new dateAlreadyTakenException(legenDate.getDate()));
		} else {
			ModelFunctions.get().addUserLegenDate(legenDate);
			logger.info("User date was updated to DB");
		}
	}

	/**
	 * adds a new character to the DB
	 * 
	 * @param legenCharacter
	 * @throws characterWithNoNameException
	 */
	public void addNewCharacter(LegenCharacter legenCharacter) throws characterWithNoNameException {
		if (legenCharacter.getCharacterName().equals("")) {
			throw (new characterWithNoNameException());
		}
		ModelFunctions.get().addNewCharacterToDB(legenCharacter);
		logger.info("New character was added to DB : " + legenCharacter.getCharacterName());
	}

	/**
	 * gets character details from DB
	 * 
	 * @param characterID
	 */
	public void getCharacterDetails(Integer characterID) {
		ModelFunctions.get().getCharacterFromDB(characterID);
	}

	/**
	 * gets character comments list from DB
	 * 
	 * @param characterID
	 */
	public void getCharacterComments(Integer characterID) {
		ModelFunctions.get().importCharacterComments(characterID);
	}

	/**
	 * adds a character comment
	 * 
	 * @param characterID
	 */
	public void addCharacterComments(Comment comment) {
		ModelFunctions.get().addCharacterComment(comment);
	}

	/**
	 * Searches for suitable characters according to the given search data
	 * 
	 * @param characterName
	 *            the name of the character to find, or "" if the user didn't
	 *            specify the name.
	 * @param gender
	 *            the gender of the character to find, or "" if the user didn't
	 *            specify the gender.
	 * @param species
	 *            the species of the character to find, or "" if the user didn't
	 *            specify the species.
	 */
	public void getSearchResults(String characterName, String gender, String species) {
		ModelFunctions.get().getSearchResults(characterName, gender, species);
	}

	/**
	 * adds new review by user for a character
	 * 
	 * @param review
	 */
	public void addNewCharacterReview(Review review) {
		ModelFunctions.get().addNewCharacterReview(review);
	}

	/**
	 * gets all review results for a character
	 * 
	 * @param characterID
	 */
	public void getCharacterReview(Integer characterID) {
		ModelFunctions.get().getCharacterReview(characterID);
	}

	/**
	 * get top ten good reviewed characters
	 */
	public void getTopTenCharacters() {
		ModelFunctions.get().getTopTenCharacters();
	}

	/**
	 * get top ten badly reviewed characters
	 */
	public void getBottomTenCharacters() {
		ModelFunctions.get().getBottomTenCharacters();
	}

	/**
	 * get top ten reviewed characters
	 */
	public void getTopTenReviewedCharacters() {
		ModelFunctions.get().getTopTenReviewedCharacters();
	}

	/**
	 * update DB from freebase
	 */
	public void updateDBFromFreebase() {
		ModelFunctions.get().updateDBFromFreebase();
	}
}
